/*
 * Project: Sudoku Explainer
 * Copyright (C) 2006-2007 Nicolas Juillerat
 * 
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules.chaining;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;

import android.net.Uri;

import com.onegravity.sudoku.solver.HintResource;
import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.R;
import com.onegravity.sudoku.solver.core.Potential;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.ImmutableCell;
import com.onegravity.sudoku.solver.util.HtmlLoader;
import com.onegravity.sudoku.solver.util.Link;
import com.onegravity.sudoku.solver.util.ValuesFormatter;

/**
 * Bidirectional Cycle hint.
 */
public class BidirectionalCycleHint extends ChainHint {

	private static final long serialVersionUID = -4874438715618165345L;
	
	private final Potential dstOn;
    private final Potential dstOff;

    // Cache
    private int _complexity = -1;

    public BidirectionalCycleHint(ChainMetaInfo metaInfo, Map<Cell, BitSet> removablePotentials, boolean isYChain, boolean isXChain, Potential dstOn, Potential dstOff) {
        super(metaInfo, removablePotentials, isYChain, isXChain);
        this.dstOn = dstOn;
        this.dstOff = dstOff;
    }

    @Override
    public boolean isWorth() {
        return mMetaInfo.mConfig.useCycles() && super.isWorth();
    }

    @Override
    public int getFlatViewCount() {
        return 2;
    }

    @Override
    public Collection<Cell> getSelectedCells(HintType type) {
        return getSelectedCellsInternal();
    }

    @Override
    protected Cell[] getSelectedCells() {
    	Collection<Cell> selectedCells = getSelectedCellsInternal();
        Cell[] result = new Cell[selectedCells.size()];
        selectedCells.toArray(result);
        return result;
    }

    private Collection<Cell> getSelectedCellsInternal() {
        Collection<Cell> cells = new LinkedHashSet<Cell>();
        Potential current = this.dstOff;
        while (!current.parents.isEmpty()) {
            current = current.parents.get(0);
            cells.add(current.cell);
        }
        return cells;
    }

    @Override
    public Map<Cell, BitSet> getAddedPotentials(HintType type, int viewNum) {
        return (type!=HintType.SMALL_CLUE) ? getColorPotentials(dstOn, viewNum==0, false) : null;
    }
    
    @Override
    public Map<Cell, BitSet> getSecondarySelectedPotentials(HintType type, int viewNum) {
        return (type!=HintType.SMALL_CLUE) ? getColorPotentials(dstOn, viewNum>0, false) : null;
    }

    @Override
    public Map<ImmutableCell, BitSet> getRemovedPotentials(HintType type) {
        return (type==HintType.HINT) ? super.getRemovedPotentials(type) : null;
    }

    @Override
    public Collection<Link> getLinks(HintType type, int viewNum) {
        Potential start = (viewNum == 0 ? this.dstOn : this.dstOff);
        return (type==HintType.HINT) ? getLinks(start) : null;
    }

    @Override
    public int getFlatComplexity() {
        if (_complexity < 0) _complexity = getAncestorCount(dstOn);
        return _complexity;
    }

    @Override
    protected Collection<Potential> getChainsTargets() {
        Collection<Potential> result = new ArrayList<Potential>(2);
        result.add(this.dstOn);
        result.add(this.dstOff);
        return result;
    }

    @Override
    protected Potential getChainTarget(int viewNum) {
        /*
         * This is only used to collect blue potential.
         * Because the reversed chain misses some causes, and
         * because the two chains are equivalent, we always
         * return the first one.
         */
        return this.dstOn;
    }

    @Override
    public int getChainingSortKey() {
        if (mIsYChain && mIsXChain) return 4;
        else if (mIsYChain) return 3;
        else return 2;
    }

    public double getDifficulty() {
        double result;
        if (mIsYChain && mIsXChain) result = 7.0;
        else result = 6.5;
        return result + getLengthDifficulty();
    }

    @Override
    protected Potential getResult() {
        return null; // No single result
    }

    @Override
    public String getName() {
        return (mIsXChain && mIsYChain) ? HintResource.BIDIRECTIONAL_CYCLE.getName() :
        	   (mIsYChain) ? HintResource.BIDIRECTIONAL_YCYCLE.getName() :
        	   (getSelectedCells().length==4) ? HintResource.BIDIRECTIONAL_XCYCLE.getName() :	// Generalized X-Wing mapped to x-cycle
        	   HintResource.BIDIRECTIONAL_XCYCLE.getName();
    }

    @Override
    public Uri getLink() {
        return (mIsXChain && mIsYChain) ? HintResource.BIDIRECTIONAL_CYCLE.getUrl() :
     	   	   (mIsYChain) ? HintResource.BIDIRECTIONAL_YCYCLE.getUrl() :
     	   	   (getSelectedCells().length==4) ? HintResource.BIDIRECTIONAL_XCYCLE.getUrl() :	// Generalized X-Wing mapped to x-cycle
     	   	   HintResource.BIDIRECTIONAL_XCYCLE.getUrl();
	}

    @Override
    public String getHintHtml(HintType type) {
    	HintResource hintRes = (mIsXChain && mIsYChain) ? HintResource.BIDIRECTIONAL_CYCLE :
    						   mIsYChain ? HintResource.BIDIRECTIONAL_YCYCLE : 
    						   HintResource.BIDIRECTIONAL_XCYCLE; 
        String result = HtmlLoader.loadHtml(hintRes.hint(type));
        Cell[] selectedCells = getSelectedCells();
		String cells = ValuesFormatter.formatCells(selectedCells, " " + HintResource.getString(R.string.and) + " "); //$NON-NLS-1$ //$NON-NLS-2$
		String value = Integer.toString(dstOn.value);
		String commonName = (!mIsYChain && selectedCells.length == 4) ? "(" + HintResource.getString(R.string.hint_generalized_xwing) + ")" : ""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		String onChain = getHtmlChain(dstOn);
		String offChain = getHtmlChain(dstOff);
		result = HtmlLoader.format(result, cells, value, commonName, onChain, offChain);
		return result;
    }

    @Override
    public String toString() {
        return getName() + ": " + Cell.toString(getSelectedCells()); //$NON-NLS-1$
    }
}